package com.gitee.wsl.mathematics.geometry.d2.rect

import com.gitee.wsl.mathematics.coordinate.d2.Coordinate2
import com.gitee.wsl.mathematics.geometry.api.RectangleShape
import com.gitee.wsl.mathematics.geometry.d2.CloseShape2d
import com.gitee.wsl.mathematics.geometry.d2.LineListShape2d
import com.gitee.wsl.mathematics.geometry.d2.line.LineShape2d
import com.gitee.wsl.mathematics.number.compareTo
import com.gitee.wsl.mathematics.vector.vec2.Vector2

interface RectangleShape2d<N:Number,V: Coordinate2<N, V>,R:RectangleShape2d<N,V,R>>:RectangleShape<N,V>,
    CloseShape2d<N, V>,LineListShape2d<N,V> {

    override val lines: List<LineShape2d<N, V>>
        get() = listOf(leftLine,
                       topLine,
                       rightLine,
                       bottomLine )

    val corner get() = proxy[0]

    /** The [x]-coordinate of the top-left corner. */
    val x get() = corner.x

    /** The [y]-coordinate of the top-left corner. */
    val y get() = corner.y

    override val center: V
        get() = (a+b)/2

    val left    : N get() = a.x
    val top     : N get() = a.y
    val right   : N get() = b.x
    val bottom  : N get() = b.y

    override val width get() = abs(a.x - b.x)
    override val height get() = abs(a.y - b.y)

    val centerX:N
        get() = center.x
    val centerY:N
        get() = center.y
    val topLeft
        get() = proxy[0]
    val topRight
        get() = createPoint2(top, right)
    val bottomLeft
        get() = createPoint2(bottom, left)
    val bottomRight
        get() = proxy[1]

    val leftLine    get() = createLine(topLeft,bottomLeft)
    val topLine     get() = createLine(topLeft,topRight)
    val rightLine   get() = createLine(topRight,bottomRight)
    val bottomLine  get() = createLine(bottomLeft,bottomRight)

    fun containsX(x: N): Boolean = x.inRange(left , right)

    fun containsY(y: N): Boolean = y.inRange(top , bottom)

    override fun containsPoint(p: Coordinate2<N, *>): Boolean = (p.x >= left && p.x < right) && (p.y >= top && p.y < bottom)

    override val area  get() = width * height
    override val perimeter  get() = (width + height) * 2

    /** Calculates the aspect ratio ( width / height ) of this rectangle
     * @return the aspect ratio of this rectangle. Returns Float.NaN if height is 0 to avoid ArithmeticException
     */
    val aspectRatio
        get() = if (height == 0f) NaN else width / height


    /** The dimensions of the [Rectangle]. */
    val dimensions
        get() = createPoint2(width, height)

    /**
     * Returns true if the viewport is empty `left >= right or bottom >= top`
     */
    val isEmpty: Boolean
        get() = left >= right || top >= bottom

    val normalized: Rectangle
        get() {
            var nx = a.x
            var ny = a.y
            if (width < 0) {
                nx += width
            }
            if (height < 0) {
                ny += height
            }
            return Rectangle(nx, ny, abs(width), abs(height))
        }

    /**
     * Returns true iff the this and the other rectangle intersect.
     *
     * @param other The second rectangle being tested for intersection
     * @return true iff the two rectangles intersect.
     */
    fun intersects(other: RectangleShape2d<N, *, *>): Boolean {
        return this.left < other.right && other.left < this.right && this.top < other.bottom && other.top < this.bottom
    }

    /**
     * Returns true iff the 4 specified sides of a viewport are inside or equal to this viewport. i.e. is this viewport
     * a superset of the specified viewport. An empty viewport never contains another viewport.
     *
     * @param left   The left side of the viewport being tested for containment
     * @param top    The top of the viewport being tested for containment
     * @param right  The right side of the viewport being tested for containment
     * @param bottom The bottom of the viewport being tested for containment
     * @return true iff the the 4 specified sides of a viewport are inside or equal to this viewport
     */
    fun contains(left: N, top: N, right: N, bottom: N): Boolean {
        // check for empty first
        return !isEmpty // now check for containment
                && this.left <= left && this.top <= top && this.right >= right && this.bottom >= bottom
    }

    /**
     * Returns true iff the specified viewport r is inside or equal to this viewport. An empty viewport never contains
     * another viewport.
     *
     * @param v The viewport being tested for containment.
     * @return true iff the specified viewport r is inside or equal to this viewport
     */
    operator fun contains(v: RectangleShape2d<N, *,*>): Boolean {
        // check for empty first
        return contains(v.left, v.top,  v.right, v.bottom)
    }

    //fun createLine(a:V,b:V):LineShape2d<N,V>

    fun create(a:V,b:V):R

    fun create(a:N,b:N,c:N,d:N) = create(
        createPoint2(a,b),
        createPoint2(c,d)
    )

    fun createWH(a:N,b:N,w:N,h:N) = create(
        createPoint2(a,b),
        createPoint2(a+w,b+h)
    )

    fun createWH(a:V,w:N,h:N) = create(
        a,
        createPoint2(a.x + w,a.y + h)
    )

    fun copy(left     :N = this.left,
             top      :N = this.top,
             right    :N = this.right,
             bottom   :N = this.bottom) = create(left, top, right, bottom)

    operator fun component1() = left

    operator fun component2() = top

    operator fun component3() = right

    operator fun component4() = bottom

    /** Sets the x and y-coordinates of the bottom left corner from vector
     * @param position The position vector
     * @return this rectangle for chaining
     */
    fun setPosition(position: Vector2<N,*>) = setPosition(position.x,position.y)

    /** Sets the x and y-coordinates of the bottom left corner
     * @param x The x-coordinate
     * @param y The y-coordinate
     * @return this rectangle for chaining
     */
    fun setPosition(x: N, y: N) = createWH(x,y,width,height)

    /** Sets the width and height of this rectangle
     * @param width The width
     * @param height The height
     * @return this rectangle for chaining
     */
    fun setSize(width: N, height: N = width) = createWH(a,width,height)

    /**
     * Creates a new [Rectangle] with sides offset both horizontally and vertically by specified amount.
     *
     * The [Rectangle] sides are shifted outwards if [offset] values are > 0 or inwards if the values are < 0.
     */
    fun offsetEdges(offset: N, offsetY: N = offset) = createWH(corner.x - offset, corner.y - offsetY, width + 2 * offset, height + 2 * offsetY)


    /** Creates a new [Rectangle] with width set to [fitWidth] and height scaled proportionally. */
    fun widthScaledTo(fitWidth: N): R {
        val scale = fitWidth / width
        return createWH(corner, fitWidth, height * scale)
    }

    /** Creates a new [Rectangle] with height set to [fitHeight] and width scaled proportionally. */
    fun heightScaledTo(fitHeight: N): R {
        val scale = fitHeight / height
        return createWH(corner, width * scale, fitHeight)
    }

}



